﻿#region Using Statements
using System;
using System.Collections.Generic;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using PloobsEngine;
using PloobsEngine.Modelo;
#endregion

namespace Primitives3D
{
    /// <summary>
    /// Base class for simple geometric primitive models. This provides a vertex
    /// buffer, an index buffer, plus methods for drawing the model. Classes for
    /// specific types of primitive (CubePrimitive, SpherePrimitive, etc.) are
    /// derived from this common base, and use the AddVertex and AddIndex methods
    /// to specify their geometry.
    /// </summary>
    public abstract class GeometricPrimitive : IDisposable, IModelo
    {

        #region textures

        String textureName = null;
        Texture2D texture = null;
        Texture2D bump = null;
        Texture2D specular = null;
        Texture2D glow = null;
        Texture2D paralax = null;
        private const string CUSTOM = "CUSTOM";

        private string _paralaxName = null;

        public string ParalaxName
        {
            get { return _paralaxName; }
            set { _paralaxName = value; }
        }

        private string _glowName = null;

        public string GlowName
        {
            get { return _glowName; }
            set { _glowName = value; }
        }

        private string _bumpName = null;

        public string BumpMapName
        {
            get { return _bumpName; }
            set { _bumpName = value; }
        }
        private string _specularName = null;

        public string SpecularMapName
        {
            get { return _specularName; }
            set { _specularName = value; }
        }

        public String DiffuseMapName
        {
            get { return textureName; }
            set { textureName = value; }
        }

        public void SetTexture(String textureName, TextureType type)
        {
            switch (type)
            {
                case TextureType.DIFFUSE:
                    this.textureName = textureName;
                    break;
                case TextureType.SPECULAR:
                    this._specularName = textureName;
                    break;
                case TextureType.BUMP:
                    this._bumpName = textureName;
                    break;
                case TextureType.GLOW:
                    this._specularName = textureName;
                    break;
                case TextureType.PARALAX:
                    this._paralaxName = textureName;
                    break;
                default:
                    throw new Exception("Invalid Buffer");
            }

            LoadModelo();
        }

        public void SetTexture(Texture2D tex, TextureType type)
        {
            switch (type)
            {
                case TextureType.DIFFUSE:
                    this.textureName = "CUSTOM";
                    this.texture = tex;
                    break;
                case TextureType.SPECULAR:
                    this.textureName = "CUSTOM";
                    this.specular = tex;
                    break;
                case TextureType.BUMP:
                    this.textureName = "CUSTOM";
                    this.bump = tex;
                    break;
                default:
                    throw new Exception("Invalid operation");
            }
        }


        private void LoadTextures()
        {
            if (!String.IsNullOrEmpty(textureName) && textureName != CUSTOM)
            {
                this.texture = EngineStuff.CustomContentManager.GetAsset<Texture2D>(textureName);
            }
            if (!String.IsNullOrEmpty(_bumpName) && _bumpName != CUSTOM)
            {
                this.bump = EngineStuff.CustomContentManager.GetAsset<Texture2D>(_bumpName);
            }
            if (!String.IsNullOrEmpty(_specularName) && _specularName != CUSTOM)
            {
                this.specular = EngineStuff.CustomContentManager.GetAsset<Texture2D>(_specularName);
            }
            if (!String.IsNullOrEmpty(_glowName) && _glowName != CUSTOM)
            {
                this.glow = EngineStuff.CustomContentManager.GetAsset<Texture2D>(_glowName);
            }
            if (!String.IsNullOrEmpty(_paralaxName) && _paralaxName != CUSTOM)
            {
                this.paralax = EngineStuff.CustomContentManager.GetAsset<Texture2D>(_paralaxName);
            }
        }

#endregion
        #region Fields


        // During the process of constructing a primitive model, vertex
        // and index data is stored on the CPU in these managed lists.
        List<VertexPositionNormalTexture> vertices = new List<VertexPositionNormalTexture>();
        List<ushort> indices = new List<ushort>();
        BoundingSphere boundingSphere;


        // Once all the geometry has been specified, the InitializePrimitive
        // method copies the vertex and index data into these buffers, which
        // store it on the GPU ready for efficient rendering.
        VertexDeclaration vertexDeclaration;

        public VertexDeclaration VertexDeclaration
        {
            get { return vertexDeclaration; }            
        }
        VertexBuffer vertexBuffer;

        public VertexBuffer VertexBuffer
        {
            get { return vertexBuffer; }           
        }
        IndexBuffer indexBuffer;

        public IndexBuffer IndexBuffer
        {
            get { return indexBuffer; }            
        }

        public int PrimitiveCount
        {
            get
            {
                return indices.Count / 3;
            }
        }

        public int VerticesCount
        {
            get
            {
                return vertices.Count;
            }
        }


        #endregion

        #region Initialization


        protected void AddVertex(Vector3 position, Vector3 normal, Vector2 textureCoord)
        {
            vertices.Add(new VertexPositionNormalTexture(position, normal,textureCoord));
        }        


        /// <summary>
        /// Adds a new index to the primitive model. This should only be called
        /// during the initialization process, before InitializePrimitive.
        /// </summary>
        protected void AddIndex(int index)
        {
            if (index > ushort.MaxValue)
                throw new ArgumentOutOfRangeException("index");

            indices.Add((ushort)index);
        }


        /// <summary>
        /// Queries the index of the current vertex. This starts at
        /// zero, and increments every time AddVertex is called.
        /// </summary>
        protected int CurrentVertex
        {
            get { return vertices.Count; }
        }


        /// <summary>
        /// Once all the geometry has been specified by calling AddVertex and AddIndex,
        /// this method copies the vertex and index data into GPU format buffers, ready
        /// for efficient rendering.
        protected void InitializePrimitive(GraphicsDevice graphicsDevice)
        {
            List<Vector3> positions = vertices.ConvertAll<Vector3>((a) => a.Position);
            boundingSphere = BoundingSphere.CreateFromPoints(positions);

            // Create a vertex declaration, describing the format of our vertex data.
            vertexDeclaration = new VertexDeclaration(EngineStuff.GraphicsDevice,
                                                VertexPositionNormalTexture.VertexElements);

            // Create a vertex buffer, and copy our vertex data into it.
            vertexBuffer = new VertexBuffer(EngineStuff.GraphicsDevice,
                                            typeof(VertexPositionNormalTexture),
                                            vertices.Count, BufferUsage.None);
            
            vertexBuffer.SetData(vertices.ToArray());

            // Create an index buffer, and copy our index data into it.
            indexBuffer = new IndexBuffer(EngineStuff.GraphicsDevice, typeof(ushort),
                                          indices.Count, BufferUsage.None);
            
            indexBuffer.SetData(indices.ToArray());

            batchInformation[0] = new BatchInformation(0, vertices.Count, PrimitiveCount, 0, 0, vertexDeclaration, vertexDeclaration.GetVertexStrideSize(0));
        }


        /// <summary>
        /// Finalizer.
        /// </summary>
        ~GeometricPrimitive()
        {
            Dispose(false);
        }


        /// <summary>
        /// Frees resources used by this object.
        /// </summary>
        public void Dispose()
        {
            Dispose(true);
            GC.SuppressFinalize(this);
        }


        /// <summary>
        /// Frees resources used by this object.
        /// </summary>
        protected virtual void Dispose(bool disposing)
        {
            if (disposing)
            {
                if (vertexDeclaration != null)
                    vertexDeclaration.Dispose();

                if (vertexBuffer != null)
                    vertexBuffer.Dispose();

                if (indexBuffer != null)
                    indexBuffer.Dispose();                
            }
        }


        #endregion

        #region IModelo Members

        public void LoadModelo()
        {
            this.InitializePrimitive(EngineStuff.GraphicsDevice);
            LoadTextures();

        }

        public void Serialize(System.Xml.XmlTextWriter textWriter)
        {            
        }

        public void DeSerialize(System.Xml.XmlNode node, EngineStuff engine)
        {         
        }

        public object getModelTag()
        {
            return null;
        }

        public ModelMesh[] getModelMeshes()
        {
            return null;
        }

        Matrix[] mi = new Matrix[] { Matrix.Identity };
        public Matrix[] getBonesTransformation()
        {
            return mi;
        }

        public Texture2D getTexture(TextureType type)
        {
            if (type == TextureType.DIFFUSE)
            {
                return this.texture;                               
            }
            else if (type == TextureType.BUMP)
            {
                return this.bump;
            }
            else if (type == TextureType.SPECULAR)
            {
                return this.specular;
            }
            else if (type == TextureType.GLOW)
            {
                return this.glow;
            }
            else if (type == TextureType.PARALAX)
            {
                return this.paralax;
            }
            else
            {
                return null;
            }



        }


        public BoundingSphere GetBoudingSphere()
        {
            return boundingSphere;
        }

        public int GetParentBoneIndex(int meshNumber)
        {
            return 0;
        }

        public int MeshNumber
        {
            get { return 1; }
        }

        public VertexBuffer GetVertexBuffer(int meshNumber)
        {
            return vertexBuffer;
        }

        public IndexBuffer GetIndexBuffer(int meshNumber)
        {
            return indexBuffer;
        }

        BatchInformation[] batchInformation = new BatchInformation [1];

        public BatchInformation[] GetBatchInformation(int meshNumber)
        {
            return batchInformation;    
        }

        public string Name
        {
            get;
            set;
        }

        #endregion
    }
}
